﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;

namespace WebApplication1.IdentityServer
{
    public class HicapAuthorizationFilter : IAuthorizationFilter
    {
        private const string SysName = "Hicap.AuthorizationServer";
        private const string NameSpacePrefix = "AppService";

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            var claims = context.HttpContext.User.Claims;
            if (claims == null || !claims.Any())
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            if (context.Filters.Any(item => item is IAllowAnonymousFilter))
            {
                return;
            }

            if (!(context.ActionDescriptor is ControllerActionDescriptor))
            {
                return;
            }

            var apiDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;

            var actionHicapAuthorizeAttribute = apiDescriptor.MethodInfo.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            var controllerHicapAuthorizeAttribute = apiDescriptor.MethodInfo.DeclaringType.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            if (actionHicapAuthorizeAttribute.Any() || controllerHicapAuthorizeAttribute.Any())
            {
                var authObjects = claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.AuthorizationDecision))?.Value.Split(",");

                if (CheckSpeicaPermission(actionHicapAuthorizeAttribute, controllerHicapAuthorizeAttribute, authObjects))
                    return;

                if (CheckPermession(context, authObjects, apiDescriptor))
                    return;
            }

            return;
        }

        private static bool CheckSpeicaPermission(IEnumerable<HicapAuthorizeAttribute> actionHicapAuthorizeAttribute,
            IEnumerable<HicapAuthorizeAttribute> controllerHicapAuthorizeAttribute, string[] authObjects)
        {
            if (actionHicapAuthorizeAttribute.Union(controllerHicapAuthorizeAttribute)
                .Any(x => authObjects.Any(y => !string.IsNullOrEmpty(x.AuthCode) && y.Equals(x.AuthCode))))
                return true;

            return false;
        }

        private static bool CheckPermession(AuthorizationFilterContext context, string[] authObjects,
            ControllerActionDescriptor apiDescriptor)
        {
            var authObjectName = apiDescriptor.ActionName;

            if (authObjects == null || authObjects.Length == 0)
                context.Result = new UnauthorizedResult();

            var controllerAuthCode = apiDescriptor.ControllerTypeInfo.FullName
                .Replace(NameSpacePrefix, string.Empty)
                .Replace(SysName, String.Empty)
                .Replace("..", ".")
                .Trim('.');

            if (authObjects.Any(x => x.Equals(SysName)) &&
                authObjects.Any(x => x.Equals(string.Join(".", controllerAuthCode, authObjectName).Trim('.'))))
                return true;
            else
                context.Result = new ForbidResult("Permissions Denied");

            return false;
        }
    }
}